package edu.yngsxy.communityservice.core.config;

import cn.hutool.core.util.StrUtil;
import edu.yngsxy.communityservice.core.annotations.ApiField;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.customizers.OperationCustomizer;
import org.springdoc.core.customizers.PropertyCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Configuration
@Slf4j
public class OpenApiConfig {

    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
                // 文档基本信息
                .info(new Info()
                        .title("社区便民生活服务平台 API 文档") // 标题
                        .description("这是一个集成示例，包含用户管理、订单接口等") // 描述
                        .version("v1.0.0") // 版本
                        // 联系人信息
                        .contact(new Contact()
                                .name("开发团队")
                                .email("dev@qyc.com")
                                .url("https://qyc.com"))
                        // 许可证信息
                        .license(new License()
                                .name("Apache 2.0")
                                .url("https://www.apache.org/licenses/LICENSE-2.0"))
                        // 概述信息
                        .summary("SpringBoot3集成Knife4j示例文档")
                        .termsOfService("https://www.wkstudy.cn")
                        // 配置版本号
                        .version("1.0")

                )
                // 定义 JWT 认证方案   配置后，Swagger UI 会显示 “Authorize” 按钮，可输入 Token 进行认证。
                .components(new Components()
                        .addSecuritySchemes("bearerAuth", new SecurityScheme()
                                .type(SecurityScheme.Type.HTTP)
                                .scheme("bearer")
                                .bearerFormat("JWT")))
                // 全局启用认证（所有接口默认需要 Token）
                .addSecurityItem(new SecurityRequirement().addList("bearerAuth"))
                ;
    }
    /**
     * OperationId 定制器
     * <p>
     * 解决SpringDoc自动为重复操作ID添加数字后缀的问题（如"update_1", "update_2"等）
     * 通过在操作生成阶段预先设置唯一且语义化的operationId，避免框架的自动去重机制
     *
     * @return OperationCustomizer Bean实例
     */
    @Bean
    public OperationCustomizer operationIdCustomizer() {
        return (operation, handlerMethod) -> {
            // 空操作检查：如果操作为空，直接返回null避免后续处理
            if (operation == null) {
                return null;
            }

            // 处理器方法检查：确保存在有效的处理器方法用于生成操作ID
            if (handlerMethod != null) {
                // 生成唯一且语义化的操作ID
                String operationId = generateUniqueOperationId(handlerMethod, operation);
                // 设置操作ID，覆盖SpringDoc可能生成的默认ID
                operation.setOperationId(operationId);
                // 记录操作ID生成日志，便于调试和监控
                //log.info("成功设置操作ID - 方法: {}, 操作ID: {}", handlerMethod.getMethod().getName(), operationId);
            }

            return operation;
        };
    }

    /**
     * 生成唯一的操作ID
     * <p>
     * 基于控制器类名、方法名和HTTP方法类型生成具有语义化的唯一操作ID。
     * 采用"操作类型 + 控制器名"的命名约定，确保ID既唯一又易于理解。
     * <p>
     * 命名规则示例：
     * - SysRoleController.list() → "listSysRole"
     * - UserController.get() → "getUser"
     * - ProductController.add() → "createProduct"
     *
     * @param handlerMethod 处理器方法，包含控制器类和方法元数据信息
     * @param operation     OpenAPI操作对象，包含HTTP方法等操作属性
     * @return 唯一且语义化的操作ID字符串
     * @throws IllegalArgumentException 当无法生成有效操作ID时抛出异常
     */
    private String generateUniqueOperationId(HandlerMethod handlerMethod, Operation operation) {
        // 参数验证
        if (handlerMethod == null || operation == null) {
            throw new IllegalArgumentException("处理器方法和操作对象不能为空");
        }

        // 1. 提取基础信息
        // 获取控制器类，用于识别业务模块
        Class<?> controllerClass = handlerMethod.getBeanType();
        // 获取方法名称，用于识别具体操作
        String methodName = handlerMethod.getMethod().getName();
        // 2. 规范化控制器名称
        // 移除常见的控制器类名后缀，保留核心业务名称
        String resourceName = normalizeControllerName(controllerClass.getSimpleName());

        // 3. 标准化方法名称
        // 将方法名转换为小写统一处理，避免大小写不一致问题
        String normalizedMethodName = methodName.toLowerCase();
        // 将各种方法名变体映射到标准的REST操作术语
        String operationType = mapToStandardOperation(normalizedMethodName);

        // 4. 生成最终操作ID
        // 组合格式：操作类型 + 控制器名（首字母大写）
        // 例如：query + SysRole = querySysRole
        resourceName = StringUtils.capitalize(resourceName);
        // 对于查询操作，在控制器名后添加"s"
        String resourcesName = operationType != null && operationType.equals("list") ? resourceName + "s" : resourceName;
        // 避免生成无效或不符合约定的操作ID
        String finalOperationId = operationType != null ? operationType + resourcesName : StrUtil.toCamelCase(methodName);
        // 5. 设置扩展属性，便于前端生产界面
        //operation.addExtension("x-operation-type", operationType);
        operation.addExtension("x-resource-name", resourceName);
        //operation.addExtension("x-original-method", methodName);

        // 验证生成的操作ID有效性
        validateOperationId(finalOperationId);

        return finalOperationId;
    }

    /**
     * 规范化控制器名称
     * <p>
     * 移除控制器类名中的常见后缀，提取核心业务名称。
     * 例如："UserController" → "User", "ProductRest" → "Product"
     *
     * @param rawControllerName 原始的控制器类名
     * @return 规范化后的控制器名称
     */
    private String normalizeControllerName(String rawControllerName) {
        if (!StringUtils.hasText(rawControllerName)) {
            return "Unknown";
        }

        // 移除常见的控制器命名后缀
        return rawControllerName
                .replace("Controller", "")  // 移除Spring MVC控制器后缀
                .replace("Rest", "")        // 移除REST风格命名后缀
                .replace("Api", "")         // 移除API接口命名后缀
                .replace("Endpoint", "");   // 移除端点命名后缀
    }

    /**
     * 将方法名映射到标准操作术语
     * <p>
     * 统一各种方法命名风格到标准的REST操作术语，确保操作ID的一致性。
     * 支持常见的方法名变体，提高代码兼容性。
     *
     * @param methodName 原始方法名称（小写）
     * @return 标准化的操作术语
     */
    private String mapToStandardOperation(String methodName) {
        return OperationMapping.getOperationType(methodName);
    }

    /**
     * 验证操作ID的有效性
     * <p>
     * 检查生成的操作ID是否符合命名规范和唯一性要求。
     * 防止生成无效或不符合约定的操作ID。
     *
     * @param operationId 待验证的操作ID
     * @throws IllegalArgumentException 当操作ID无效时抛出异常
     */
    private void validateOperationId(String operationId) {
        if (!StringUtils.hasText(operationId)) {
            throw new IllegalArgumentException("生成的操作ID不能为空");
        }

        // 检查操作ID长度限制（OpenAPI规范建议）
        if (operationId.length() > 50) {
            log.warn("操作ID长度超过建议值: {}", operationId);
        }

        // 检查是否包含非法字符
        if (!operationId.matches("^[a-zA-Z][a-zA-Z0-9]*$")) {
            throw new IllegalArgumentException("操作ID只能包含字母和数字，且必须以字母开头: " + operationId);
        }
    }

    /**
     * 操作名称映射常量类。准REST操作术语映射
     * * 将各种方法名映射到符合RESTful规范的标准术语
     */
    private static class OperationMapping {
        // 使用静态不可变Map，避免重复创建
        private static final Map<String, String> STANDARD_REST_OPERATION_MAPPING = Map.ofEntries(
                // ==================== 查询操作 ====================
                Map.entry("list", "list"),      // 列表查询 - 标准
                Map.entry("find", "list"),      // 查找 → 列表
                Map.entry("search", "list"),      // 搜索 → 列表
                Map.entry("query", "list"),      // 查询 → 列表
                Map.entry("select", "list"),      // 选择 → 列表
                Map.entry("page", "list"),      // 分页 → 列表
                Map.entry("findall", "list"),      // 查找所有 → 列表
                Map.entry("getall", "list"),      // 获取所有 → 列表
                Map.entry("fetch", "list"),      // 获取 → 列表
                Map.entry("retrieve", "list"),      // 检索 → 列表

                // ==================== 获取单个资源 ====================
                Map.entry("get", "get"),       // 获取单个 - 标准
                Map.entry("findbyid", "get"),       // 按ID查找 → 获取
                Map.entry("selectbyid", "get"),       // 按ID选择 → 获取
                Map.entry("getbyid", "get"),       // 按ID获取 → 获取
                Map.entry("detail", "get"),       // 详情 → 获取
                Map.entry("info", "get"),       // 信息 → 获取
                Map.entry("view", "get"),       // 查看 → 获取
                Map.entry("show", "get"),       // 显示 → 获取
                Map.entry("load", "get"),       // 加载 → 获取
                Map.entry("fetchbyid", "get"),       // 按ID获取 → 获取

                // ==================== 创建操作 ====================
                Map.entry("create", "create"),    // 创建 - 标准
                Map.entry("add", "create"),    // 新增 → 创建
                Map.entry("save", "create"),    // 保存 → 创建
                Map.entry("insert", "create"),    // 插入 → 创建
                Map.entry("new", "create"),    // 新建 → 创建
                //Map.entry("register", "create"),    // 注册 → 创建
                //Map.entry("generate", "create"),    // 生成 → 创建
                Map.entry("build", "create"),    // 构建 → 创建
                Map.entry("make", "create"),    // 制作 → 创建
                //Map.entry("init", "create"),    // 初始化 → 创建

                // ==================== 更新操作 ====================
                Map.entry("update", "update"),    // 更新 - 标准
                Map.entry("modify", "update"),    // 修改 → 更新
                Map.entry("edit", "update"),    // 编辑 → 更新
                Map.entry("change", "update"),    // 更改 → 更新
                Map.entry("adjust", "update"),    // 调整 → 更新
                Map.entry("alter", "update"),    // 改变 → 更新
                Map.entry("revise", "update"),    // 修订 → 更新
                Map.entry("amend", "update"),    // 修正 → 更新
                Map.entry("correct", "update"),    // 纠正 → 更新
                //Map.entry("refresh", "update"),    // 刷新 → 更新

                // ==================== 删除操作 ====================
                Map.entry("delete", "delete"),    // 删除 - 标准
                Map.entry("remove", "delete"),    // 移除 → 删除
                Map.entry("destroy", "delete"),    // 销毁 → 删除
                Map.entry("del", "delete"),    // 删除简写 → 删除
                Map.entry("cancel", "delete"),    // 取消 → 删除
                Map.entry("drop", "delete"),    // 丢弃 → 删除
                Map.entry("erase", "delete"),    // 擦除 → 删除
                Map.entry("clear", "delete"),    // 清除 → 删除
                Map.entry("purge", "delete"),    // 清理 → 删除
                //Map.entry("unregister", "delete"),    // 注销 → 删除

                // ==================== 部分更新操作 ====================
                Map.entry("patch", "patch"),     // 部分更新 - 标准

                // ==================== 验证操作 ====================
                Map.entry("validate", "validate"),  // 验证 - 标准
                Map.entry("verify", "validate"),  // 校验 → 验证
                Map.entry("check", "validate"),  // 检查 → 验证
                Map.entry("audit", "validate"),  // 审计 → 验证

                // ==================== 状态操作 ====================
                Map.entry("enable", "enable"),    // 启用 - 标准
                Map.entry("disable", "disable"),   // 禁用 - 标准
                Map.entry("activate", "enable"),    // 激活 → 启用
                Map.entry("deactivate", "disable"),   // 停用 → 禁用
                Map.entry("start", "enable"),    // 启动 → 启用
                Map.entry("stop", "disable"),   // 停止 → 禁用
                Map.entry("open", "enable"),    // 开启 → 启用
                Map.entry("close", "disable"),   // 关闭 → 禁用

                // ==================== 文件操作 ====================
                Map.entry("upload", "upload"),    // 上传 - 标准
                Map.entry("download", "download"),  // 下载 - 标准
                Map.entry("export", "export"),    // 导出 - 标准
                Map.entry("import", "import"),    // 导入 - 标准

                // ==================== 业务操作 ====================
                Map.entry("approve", "approve"),   // 审批 - 标准
                Map.entry("reject", "reject"),    // 拒绝 - 标准
                Map.entry("submit", "submit"),    // 提交 - 标准
                Map.entry("publish", "publish"),   // 发布 - 标准
                Map.entry("unpublish", "unpublish"), // 取消发布 - 标准
                Map.entry("lock", "lock"),      // 锁定 - 标准
                Map.entry("unlock", "unlock"),    // 解锁 - 标准
                Map.entry("reset", "reset"),     // 重置 - 标准
                Map.entry("restore", "restore"),   // 恢复 - 标准
                Map.entry("archive", "archive")    // 归档 - 标准
        );

        static String getOperationType(String methodName) {
            return STANDARD_REST_OPERATION_MAPPING.getOrDefault(methodName, null);
        }
    }


    /**
     * 创建属性定制器Bean，用于自定义Swagger模型属性的生成
     * 主要功能：为带有@ApiField注解的属性添加排序扩展信息
     *
     * @return PropertyCustomizer 属性定制器实例
     */
    @Bean
    public PropertyCustomizer propertyCustomizer() {
        // 返回PropertyCustomizer函数式接口的实现
        return (property, type) -> {
            // 从类型上下文的注解数组中查找ApiField注解
            ApiField apiField = findApiFieldInCtxAnnotations(type.getCtxAnnotations());

            // 如果找到了ApiField注解
            if (apiField != null) {
                // 将ApiField注解中的order值添加到Swagger属性的扩展中
                // "x-order"是Swagger扩展属性，用于控制字段在文档中的显示顺序
                property.addExtension("x-order", apiField.order());
            }

            // 返回处理后的属性对象
            return property;
        };
    }

    /**
     * 从注解数组中查找ApiField注解
     * 该方法会遍历注解数组，返回第一个找到的ApiField注解实例
     *
     * @param ctxAnnotations 注解数组，包含字段或参数上的所有注解
     * @return ApiField 找到的ApiField注解实例，如果未找到则返回null
     */
    private ApiField findApiFieldInCtxAnnotations(Annotation[] ctxAnnotations) {
        // 检查注解数组是否为空，避免空指针异常
        if (ctxAnnotations == null) {
            return null;
        }

        // 遍历注解数组中的每个注解
        for (Annotation annotation : ctxAnnotations) {
            // 方式1：使用instanceof操作符直接判断注解类型
            // 这是最直接的类型判断方式
            if (annotation instanceof ApiField) {
                return (ApiField) annotation;
            }

            // 方式2：使用annotationType()方法判断注解类型
            // 这种方式通过比较注解的Class对象来确认类型
            if (annotation.annotationType().equals(ApiField.class)) {
                return (ApiField) annotation;
            }
        }

        // 如果遍历完所有注解都没有找到ApiField注解，返回null
        return null;
    }
}
